import Rect, { getRect } from './rect';
import { RectAttr, SpacingMarker as SpacingMarkerType } from './type';
import { createPlaceholder, cleanPlaceholder, initPlaceholderElement } from './placeholder';
import { initMarks, placeMark, cleanMarks } from './marker';

let hoveringElement: HTMLElement | null;
let selectedElement: HTMLElement | null;
let targetElement: HTMLElement | null;

const SpacingMarker: SpacingMarkerType = {
  el: document.body,
  appendToEl: document.body,
  useBoundingClientRect: true,
  selectedColor: '#2196F3',
  targetColor: '#fe1d5f',
  zIndex: 10000,
  scale: 1,
  onRangingChange: () => {},
  start(el, appendToEl) {
    if (!document.body) {
      console.warn(`Unable to initialise, document.body does not exist.`);
      return;
    }
    this.el = el || document.body;
    this.appendToEl = appendToEl || document.body;
    this.el.addEventListener('mousedown', mouseDownHandler);
    this.el.addEventListener('mousemove', cursorMovedHandler);
    initPlaceholderElement();
    initMarks();
  },
  end(el) {
    const targetEl = el || this.el;
    targetEl.removeEventListener('mousedown', mouseDownHandler);
    targetEl.removeEventListener('mousemove', cursorMovedHandler);
    cleanAll();
  },
  updateScale(scale: number) {
    this.scale = scale;
    cleanPlaceholder('selected');
    cleanPlaceholder('target');
    cleanMarks();
    targetElement = null;
    if (selectedElement) {
      let rect = getRect(selectedElement);
      createPlaceholder('selected', rect.width, rect.height, rect.top, rect.left, selectedElement.style);
    }
  },
  clean() {
    cleanAll();
  },
};

function mouseDownHandler(e: MouseEvent) {
  // 仅鼠标左键有效
  if (e.buttons !== 1) return;

  cleanAll();

  setHoveringElement(e);

  setSelectedElement();
}

function cursorMovedHandler(e: MouseEvent) {
  setHoveringElement(e);

  setTargetElement();

  if (!selectedElement || !targetElement) return;
  // Do the calculation
  let selectedElementRect: RectAttr = getRect(selectedElement);
  let targetElementRect: RectAttr = getRect(targetElement);

  let selected: Rect = new Rect(selectedElementRect);
  let target: Rect = new Rect(targetElementRect);

  cleanMarks();

  let top: number, bottom: number, left: number, right: number, outside: boolean;

  if (selected.containing(target) || selected.inside(target) || selected.colliding(target)) {
    // console.log(
    //   `containing: ${selected.containing(target)} || inside: ${selected.inside(
    //     target,
    //   )} || colliding: ${selected.colliding(target)}`,
    // );
    outside = false;
    top = Math.abs(selectedElementRect.top - targetElementRect.top);
    bottom = Math.abs(selectedElementRect.bottom - targetElementRect.bottom);
    left = Math.abs(selectedElementRect.left - targetElementRect.left);
    right = Math.abs(selectedElementRect.right - targetElementRect.right);
  } else {
    // console.log(`outside`);
    outside = true;
    // 纵轴
    if (selectedElementRect.bottom > targetElementRect.top) {
      top = Math.abs(selectedElementRect.top - targetElementRect.top);
    } else {
      top = Math.abs(selectedElementRect.bottom - targetElementRect.top);
    }
    if (selectedElementRect.top > targetElementRect.bottom) {
      bottom = Math.abs(selectedElementRect.top - targetElementRect.bottom);
    } else {
      bottom = Math.abs(selectedElementRect.bottom - targetElementRect.bottom);
    }
    // 横轴
    if (selectedElementRect.right > targetElementRect.left) {
      left = Math.abs(selectedElementRect.left - targetElementRect.left);
    } else {
      left = Math.abs(selectedElementRect.right - targetElementRect.left);
    }
    if (selectedElementRect.left > targetElementRect.right) {
      right = Math.abs(selectedElementRect.left - targetElementRect.right);
    } else {
      right = Math.abs(selectedElementRect.right - targetElementRect.right);
    }
  }

  placeMark(selected, target, 'top', top, outside);
  placeMark(selected, target, 'bottom', bottom, outside);
  placeMark(selected, target, 'left', left, outside);
  placeMark(selected, target, 'right', right, outside);

  SpacingMarker.onRangingChange({ top, bottom, left, right });
}

function setHoveringElement(e: MouseEvent): void {
  if (e.composedPath) {
    // Use composedPath to detect the hovering element for supporting shadow DOM
    hoveringElement = e.composedPath()[0] as HTMLElement;
  } else {
    // Fallback if not support composedPath
    hoveringElement = e.target as HTMLElement;
  }
}

function setSelectedElement(): void {
  if (!hoveringElement) return;
  if (hoveringElement === selectedElement) return;
  selectedElement = hoveringElement;
  cleanPlaceholder('selected');

  let rect = getRect(selectedElement);

  createPlaceholder('selected', rect.width, rect.height, rect.top, rect.left, selectedElement.style);
}

function setTargetElement(): void {
  if (!hoveringElement) return;
  if (hoveringElement === selectedElement) return;
  if (hoveringElement === targetElement) return;
  targetElement = hoveringElement;

  cleanPlaceholder('target');

  let rect = getRect(targetElement);

  createPlaceholder('target', rect.width, rect.height, rect.top, rect.left, targetElement.style);
}

function cleanAll(): void {
  cleanPlaceholder('selected');
  cleanPlaceholder('target');
  cleanMarks();

  selectedElement = null;
  targetElement = null;

  SpacingMarker.onRangingChange({ top: 0, bottom: 0, left: 0, right: 0 });
}

export default SpacingMarker;
